import React, { ReactNode, useEffect, useState, useRef, useMemo } from 'react';
import { Form, Modal, Drawer, Button } from 'antd';
import type { FormInstance, ModalProps, DrawerProps } from 'antd';

export interface modalRefArg<T extends 'modal' | 'drawer', R extends Record<string, any>> {
    show: showFunction<T, R>;
}

export type submitFunction<T extends Record<string, any>> = (
    formValue: Record<string, any>,
    params: T,
    contentData: any
) => Promise<any>;
export type onShowFunction<T extends Record<string, any>, R extends 'modal' | 'drawer'> = (
    params: T,
    props: R extends 'modal' ? ModalProps : DrawerProps,
    form: FormInstance<any>
) => void;

interface renderParams {
    form: FormInstance<any>;
    visible: boolean;
    params: Record<string, any>;
    cancel: cancelFunction;
    forwardParams: (data: any) => void;
}

type cancelFunction = (...rest: any[]) => void;
type showFunction<T, R> = (
    visible: boolean,
    params?: R,
    props?: T extends 'modal' ? ModalProps : DrawerProps
) => void;
type renderFunction = (params: renderParams, props: ContentProps) => ReactNode;

interface WrapperProps {
    /* 弹框的类型，弹框、抽屉 */
    modalType: 'modal' | 'drawer';
    /* 弹框的底部区域是否展示，none 只针对抽屉 */
    footType?: 'onlyClose' | 'none';
    /* 弹框的底部区域，支持JSX，也支持函数会传入必要参数及组件的props */
    customFooter?: renderFunction | ReactNode;
    /* 给children 的类名 */
    childrenClassName?: string;
    children?: ReactNode;
    /* 暴露出去控制弹框显示的处理函数，目前只暴露了一个show方法 */
    modalRef?: (ref: modalRefArg<WrapperProps['modalType'], Record<string, any>>) => void;
    /* 弹框内容的渲染函数，会传入必要参数及组件的props */
    render: renderFunction;
    /* 监听弹框显示隐藏的回调 */
    onChange?: (visible: boolean) => void;
    /* 初始化渲染的回调函数，将show函数的参数及form暴露 */
    onShow?: onShowFunction<any, WrapperProps['modalType']>;
    /* 提交的回调函数，会收集表单数据以及传入的参数 */
    submit?: submitFunction<any>;
}

interface ContentProps extends Omit<WrapperProps, 'modalRef'> {
    form: FormInstance<any>;
    visible: boolean;
    loading: boolean;
    params: Record<string, any>;
    forwardParams: (data: any) => void;
    handleAfterClose: () => void;
    ok: () => void;
    cancel: cancelFunction;
}

const ModalContent: React.FC<ContentProps> = (props) => {
    const {
        loading,
        visible,
        modalType,
        params,
        form,
        footType,
        customFooter,
        render,
        cancel,
        ok,
        forwardParams,
        handleAfterClose,
        ...other
    } = props;

    const renderFooter = () => {
        if (customFooter === null) return null;

        if (customFooter) {
            if (typeof customFooter === 'function') {
                return customFooter(
                    {
                        form,
                        visible,
                        params,
                        cancel,
                        forwardParams,
                    },
                    props
                );
            }

            return customFooter;
        }

        if (modalType === 'modal') {
            if (footType === 'onlyClose') {
                return <Button onClick={cancel}>关闭</Button>;
            }

            return undefined;
        }

        if (modalType === 'drawer') {
            return (
                <div
                    style={{
                        width: '100%',
                        position: 'absolute',
                        bottom: 0,
                        left: 0,
                        zIndex: 999,
                        background: '#fff',
                        borderTop: '1px solid #ccc',
                        textAlign: 'right',
                        padding: '10px',
                    }}
                >
                    {footType !== 'onlyClose' && (
                        <Button
                            type='primary'
                            loading={loading}
                            onClick={ok}
                        >
                            提交
                        </Button>
                    )}
                    <Button
                        style={{ marginLeft: '10px' }}
                        onClick={cancel}
                    >
                        关闭
                    </Button>
                </div>
            );
        }

        return undefined;
    };

    const RenderContent = useMemo(() => {
        if (!visible) return null;

        return render(
            {
                form,
                visible,
                params,
                cancel,
                forwardParams,
            },
            props
        );
    }, [visible, params]);

    if (modalType === 'drawer') {
        return (
            <Drawer
                width='66%'
                placement='right'
                open={visible}
                onClose={cancel}
                afterOpenChange={(open) => {
                    if (open) return;

                    handleAfterClose();
                }}
                {...other}
            >
                {RenderContent}
                {footType === 'none' ? null : renderFooter()}
            </Drawer>
        );
    }

    return (
        <Modal
            okText='确定'
            cancelText='取消'
            confirmLoading={loading}
            open={visible}
            footer={renderFooter()}
            onCancel={cancel}
            onOk={ok}
            afterClose={handleAfterClose}
            {...other}
        >
            {RenderContent}
        </Modal>
    );
};

const Wrapper: React.FC<WrapperProps> = (props) => {
    const { childrenClassName, children, submit, modalRef, onChange, onShow, render, ...other } =
        props;

    const [form] = Form.useForm();

    const [visible, setVisible] = useState(false);
    const [isClose, setIsClose] = useState(true);
    const [loading, setLoading] = useState(false);
    const [contentParams, setContentParams] = useState({});
    const [contentProps, setContentProps] = useState({});
    const contentDataRef = useRef({});

    useEffect(() => {
        modalRef && modalRef({ show: handleShow });
    }, []);

    useEffect(() => {
        if (visible === false) {
            cancel();
        }
        onChange && onChange(visible);
    }, [visible]);

    const handleShow: showFunction<WrapperProps['modalType'], Record<string, any>> = (
        visible,
        params,
        props
    ) => {
        setVisible(visible);
        setIsClose(true);

        if (params) {
            setContentParams(params);
        }

        if (props) {
            setContentProps(props);
        }

        onShow && onShow(params || {}, props || {}, form);
    };

    const cancel = () => {
        if (!visible) {
            // 对于请求一直没响应关闭弹框导致一直加载的问题
            setLoading(false);
            return;
        }

        form.resetFields();
        setVisible(false);
        setContentParams({});
        setContentProps({});
        contentDataRef.current = {};
    };

    const ok = () => {
        new Promise((resolve, reject) => {
            form.validateFields()
                .then(() => {
                    setLoading(true);
                    resolve(form.getFieldsValue());
                })
                .catch((err) => {
                    reject(err);
                });
        })
            .then((formValue) => {
                return (
                    submit &&
                    submit(formValue as Record<string, any>, contentParams, contentDataRef.current)
                );
            })
            .then((res) => {
                if (res) cancel();
            })
            .finally(() => {
                setLoading(false);
            });
    };

    const forwardParams = (data: any) => {
        contentDataRef.current = data;
    };

    const handleAfterClose = () => {
        setIsClose(false);
    };

    return (
        <div>
            {children && (
                <div
                    className={childrenClassName}
                    onClick={(e) => {
                        e.stopPropagation();
                        setVisible(true);
                    }}
                >
                    {children}
                </div>
            )}
            {isClose && (
                <ModalContent
                    form={form}
                    visible={visible}
                    loading={loading}
                    params={contentParams}
                    cancel={cancel}
                    ok={ok}
                    render={render}
                    forwardParams={forwardParams}
                    handleAfterClose={handleAfterClose}
                    {...other}
                    // show方法传入的props 优先级更高，会覆盖组件所传入的props
                    {...contentProps}
                />
            )}
        </div>
    );
};

type WrapperPropsToOmit = Omit<WrapperProps, 'modalType'>;

/* 弹框的props 需要继承 ModalProps */
interface ModalWrapperProps extends WrapperPropsToOmit, ModalProps {
    modalRef?: (ref: modalRefArg<'modal', Record<string, any>>) => void;
}

/* 抽屉的props 需要继承 DrawerProps */
interface DrawerWrapperProps extends WrapperPropsToOmit, DrawerProps {
    modalRef?: (ref: modalRefArg<'drawer', Record<string, any>>) => void;
}

export const ModalWrapper: React.FC<ModalWrapperProps> = (props) => (
    <Wrapper
        modalType='modal'
        {...props}
    />
);
export const DrawerWrapper: React.FC<DrawerWrapperProps> = (props) => (
    <Wrapper
        modalType='drawer'
        {...props}
    />
);
